﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DotSpatial.Topology;

namespace HAGISV2
{
    public class TopologyTool
    {
        /// <summary>
        /// Polygon转PolyShape 无id信息
        /// </summary>
        /// <param name="geo"></param>
        /// <returns></returns>
        public static PolyShape  PolygonToFetureObject(Polygon polygon, string polylineKey)
        {
            //外环坐标
            List<Coordinate> coords = new List<Coordinate>();

            //洞坐标
            List<List<Coordinate>> innerHoles = new List<List<Coordinate>>();

            IList<Coordinate> coorLst = polygon.ExteriorRing.Coordinates;
            foreach (Coordinate item in coorLst)
            {
                coords.Add(new Coordinate(item.X, item.Y));
            }
            coords.RemoveAt(coords.Count - 1);


            foreach (ILinearRing linearRing in polygon.Holes)
            {
                List<Coordinate> innerhole = new List<Coordinate>();

                IList<Coordinate> lr = linearRing.Coordinates;
                foreach (Coordinate item in lr)
                {
                    innerhole.Add(new Coordinate(item.X, item.Y));
                }
                innerhole.RemoveAt(innerhole.Count - 1);
                innerHoles.Add(innerhole);
            }

            return new PolyShape("", polylineKey, coords.ToArray(), innerHoles);
        }





        /// <summary>
        /// 获取切割后的图形，可能返回null 如果交点不为两个
        /// </summary>
        /// <param name="plg"></param>
        /// <param name="ls"></param>
        /// <param name="plgs">two</param>
        /// <returns></returns>
        public static bool GetTwoFinalPolygon(Polygon plg, LineString ls, ref Polygon[] plgs)
        {
            if (plg == null || ls== null)  return false;
            plgs = new Polygon[] { };

            DotSpatial.Topology.Point point0 = new DotSpatial.Topology.Point();
            DotSpatial.Topology.Point point1 = new DotSpatial.Topology.Point();
            IGeometry ig;

            ILinearRing lr = (LinearRing)plg.Shell;

            ig = lr.Intersection(ls);
            if (ig.Coordinates.Count != 2) return false;
            //Vector vect = new Vector(ig.Coordinates[0], ig.Coordinates[1]);
            //两交点未切  
            //if (!plg.Contains(new DotSpatial.Topology.Point(ig.Coordinates[0] + (0.5 * vect).ToCoordinate())))
            //    return false;

            List<Coordinate> twoSeg = new List<Coordinate>(4);
            List<Coordinate> innerCoords = new List<Coordinate>();
            LineString helpString;

            int interSectionCount = 0;
            int outTimes = 0;
            for (int i = 1; i < ls.Count; i++)
            {
                helpString = new LineString(new List<Coordinate>() { ls.Coordinates[i - 1], ls.Coordinates[i] });


                if (
                    (!lr.Intersects(helpString) && !plg.Contains(helpString))
                    || (plg.Contains(new DotSpatial.Topology.Point(ls.Coordinates[i - 1])) && outTimes == 0)
                    )
                    continue;

                //被一条线段切成两个的情况
                IGeometry igeo = lr.Intersection(helpString);
                if (igeo.Coordinates.Count >= 2 && igeo.Coordinates[0] != igeo.Coordinates[1])
                {
                    Vector vect = new Vector(igeo.Coordinates[0], igeo.Coordinates[1]);
                    if (!plg.Contains(new DotSpatial.Topology.Point(igeo.Coordinates[0] + (0.5 * vect).ToCoordinate())))
                        return false;
                    for (int j = 1; j < lr.Coordinates.Count; j++)
                    {
                        if (GetIntersectCoord(lr.Coordinates[j - 1], lr.Coordinates[j], ls.Coordinates[i - 1], ls.Coordinates[i], ref interSectionCount) != null)
                        {
                            innerCoords.Add(GetIntersectCoord(lr.Coordinates[j - 1], lr.Coordinates[j], ls.Coordinates[i - 1], ls.Coordinates[i], ref interSectionCount));
                            twoSeg.Add(lr.Coordinates[j - 1]);
                            twoSeg.Add(lr.Coordinates[j]);
                        }
                    }
                    plgs = GetTwoCutedPolygons(twoSeg, innerCoords, plg);
                    break;
                }
                point0 = new DotSpatial.Topology.Point(ls.Coordinates[i - 1]);
                point1 = new DotSpatial.Topology.Point(ls.Coordinates[i]);
                //其它情况
                for (int j = 1; j < lr.Coordinates.Count; j++)
                {


                    if (outTimes % 2 == 0)
                    {
                        if (!plg.Contains(point0) && plg.Contains(point1)
                            && (GetIntersectCoord(lr.Coordinates[j - 1], lr.Coordinates[j], ls.Coordinates[i - 1], ls.Coordinates[i], ref interSectionCount) != null))
                        {
                            twoSeg.Clear();
                            twoSeg.Add(lr.Coordinates[j - 1]);
                            twoSeg.Add(lr.Coordinates[j]);
                            outTimes++;
                            innerCoords.Clear();
                            innerCoords.Add(GetIntersectCoord(lr.Coordinates[j - 1], lr.Coordinates[j], ls.Coordinates[i - 1], ls.Coordinates[i], ref interSectionCount));
                        }
                    }
                    else if (outTimes % 2 == 1)
                    {
                        Coordinate intersect = GetIntersectCoord(lr.Coordinates[j - 1], lr.Coordinates[j], ls.Coordinates[i - 1], ls.Coordinates[i], ref interSectionCount);
                        if (plg.Contains(point0) && !plg.Contains(point1)
                            && (intersect != null))
                        {
                            twoSeg.Add(lr.Coordinates[j - 1]);
                            twoSeg.Add(lr.Coordinates[j]);
                            outTimes++;
                            innerCoords.Add(ls.Coordinates[i - 1]);
                            innerCoords.Add(GetIntersectCoord(lr.Coordinates[j - 1], lr.Coordinates[j], ls.Coordinates[i - 1], ls.Coordinates[i], ref interSectionCount));
                            plgs = GetTwoCutedPolygons(twoSeg, innerCoords, plg);
                            break;
                        }

                    }



                }
                if (plg.Contains(point0) && plg.Contains(point1))
                {
                    innerCoords.Add((Coordinate)ls.Coordinates[i - 1].Clone());
                }
                if (plgs.Length == 2)
                    break;

            }
            if (plgs != null)
                return true;
            return false;
        }

        /// <summary>
        /// 添加洞
        /// </summary>
        /// <param name="plg"></param>
        /// <param name="holes"></param>
        /// <param name="holePolygon"></param>
        /// <returns></returns>
        public static bool AddHolesForPolygon(Polygon plg, LinearRing[] holes, out Polygon holePolygon)
        {
            holePolygon = new Polygon(new LinearRing(new Coordinate[] { }));
            for (int i = 0; i < holes.Length; i++)
            {
                if (!plg.Contains(holes[i]))
                {
                    return false;
                }

            }
            holePolygon = new Polygon(plg.Shell, holes);
            return true;
        }



        #region 内部函数

        /// <summary>
        /// Cut to Two
        /// </summary>
        /// <param name="fourCoords">
        ///   1 | 2  
        /// ----------线段四端点
        ///   0 | 3  
        /// </param>
        /// <param name="innerCoords">The ends is two intersections and contain innerCoordinates in the middle</param>
        /// <param name="cuttingPolygon">The polygon being cutted</param>
        /// <returns></returns>
        private static Polygon[] GetTwoCutedPolygons(List<Coordinate> fourCoords, List<Coordinate> innerCoords, Polygon cuttingPolygon)
        {
            int a, b, c, d;
            LinearRing lr = (LinearRing)cuttingPolygon.Shell;
            List<Coordinate> shellCoords1 = new List<Coordinate>();
            List<Coordinate> shellCoords2 = new List<Coordinate>();
            Polygon[] twoPolygons = new Polygon[2];


            a = lr.Coordinates.IndexOf(fourCoords[0]);
            b = lr.Coordinates.IndexOf(fourCoords[1]);
            c = lr.Coordinates.IndexOf(fourCoords[2]);
            d = lr.Coordinates.IndexOf(fourCoords[3]);
            if (b == 0) b = lr.Coordinates.Count - 1;
            if (d == 0) d = lr.Coordinates.Count - 1;

            #region 排序
            int trans = a;

            if (a > c)
            {
                a = c; c = trans;
                trans = b;
                b = d; d = trans;
                innerCoords.Reverse();
            }
            #endregion

            #region 交于一条线的情况
            if (a == c)
            {
                shellCoords1.AddRange(innerCoords);
                shellCoords2.AddRange(innerCoords);
                if (fourCoords[0].Distance(innerCoords[0]) > fourCoords[0].Distance(innerCoords[innerCoords.Count - 1]))
                {
                    if (fourCoords[0] != innerCoords[innerCoords.Count - 1])
                        shellCoords2.Add(fourCoords[0]);
                    for (int i = a - 1; i > 0; i--)
                    {
                        shellCoords2.Add(lr.Coordinates[i]);
                    }
                    for (int i = lr.Coordinates.Count - 1; i > b; i--)
                    {
                        shellCoords2.Add(lr.Coordinates[i]);
                    }
                    if (fourCoords[1] != innerCoords[0])
                        shellCoords2.Add(fourCoords[1]);
                }
                else
                {
                    if (fourCoords[1] != innerCoords[innerCoords.Count - 1])
                        shellCoords2.Add(fourCoords[1]);
                    for (int i = b + 1; i < lr.Coordinates.Count - 1; i++)
                    {
                        shellCoords2.Add(lr.Coordinates[i]);
                    }
                    for (int i = 0; i < a; i++)
                    {
                        shellCoords2.Add(lr.Coordinates[i]);
                    }
                    if (fourCoords[0] != innerCoords[0])
                        shellCoords2.Add(fourCoords[0]);

                }


                twoPolygons[0] = new Polygon(new LinearRing(shellCoords1));
                twoPolygons[1] = new Polygon(new LinearRing(shellCoords2));

                return twoPolygons;
            }
            #endregion


            #region 生成图形顺序点

            //第一个
            shellCoords1.AddRange(innerCoords);
            //端点
            if (innerCoords[innerCoords.Count - 1] != fourCoords[3] && d != 0)
                shellCoords1.Add(lr.Coordinates[d]);

            for (int i = c + 2; i < lr.Coordinates.Count - 1; i++)
            {
                shellCoords1.Add(lr.Coordinates[i]);
            }
            for (int i = 0; i < a; i++)
            {
                shellCoords1.Add(lr.Coordinates[i]);
            }
            if (innerCoords[0] != lr.Coordinates[a])
                shellCoords1.Add(lr.Coordinates[a]);

            //第二个
            shellCoords2.AddRange(innerCoords);
            //端点
            if (innerCoords[innerCoords.Count - 1] != fourCoords[2])
                shellCoords2.Add(lr.Coordinates[c]);

            for (int i = c - 1; i > b; i--)
            {
                shellCoords2.Add(lr.Coordinates[i]);
            }
            if (innerCoords[0] != lr.Coordinates[b])
                shellCoords2.Add(lr.Coordinates[b]);

            #endregion

            twoPolygons[0] = new Polygon(new LinearRing(shellCoords1));
            twoPolygons[1] = new Polygon(new LinearRing(shellCoords2));

            List<Polygon> polyHoles = new List<Polygon>();

            foreach (ILinearRing item in cuttingPolygon.Holes)
            {
                polyHoles.Add(new Polygon(item));
            }
            foreach (Polygon item in polyHoles)
            {
                if (twoPolygons[0].Contains(item))
                    twoPolygons[0] = (Polygon)twoPolygons[0].Difference(item);
                else
                    twoPolygons[1] = (Polygon)twoPolygons[1].Difference(item);
            }



            return twoPolygons;

        }


        /// <summary>
        /// Get intersection coordinate of two segment
        /// </summary>
        /// <param name="shellcoord1"></param>
        /// <param name="shellcoord2"></param>
        /// <param name="linecoord1"></param>
        /// <param name="linecoord2"></param>
        /// <param name="interSectionCount">Now isn't useful, just for extend</param>
        /// <returns>coordinate</returns>
        private static Coordinate GetIntersectCoord(Coordinate shellcoord1, Coordinate shellcoord2, Coordinate linecoord1, Coordinate linecoord2, ref int interSectionCount)
        {
            LineSegment shellSeg = new LineSegment(shellcoord1, shellcoord2);
            LineSegment lineSeg = new LineSegment(linecoord1, linecoord2);


            Coordinate interCoord = shellSeg.Intersection(lineSeg);
            if (interCoord != null) interSectionCount++;
            return interCoord;
        }


        #endregion

    }
}
